package com.cqhfan.chatroom.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cqhfan.chatroom.domain.dto.LoginFormDTO;
import com.cqhfan.chatroom.domain.po.User;
import com.cqhfan.chatroom.domain.vo.CaptchaVO;
import com.cqhfan.chatroom.domain.vo.UserInfoVO;
import com.cqhfan.chatroom.domain.vo.UserLoginVO;
import com.cqhfan.chatroom.enums.LoginType;
import com.cqhfan.chatroom.enums.UserStatus;
import com.cqhfan.chatroom.mapper.UserMapper;
import com.cqhfan.chatroom.service.ITokenService;
import com.cqhfan.chatroom.service.IUserService;
import com.cqhfan.common.autoconfigure.cache.MultiLevelCache;
import com.cqhfan.common.constants.BizConstants;
import com.cqhfan.common.constants.RedisConstants;
import com.cqhfan.common.exceptions.BizException;
import com.cqhfan.common.utils.MD5Util;
import com.cqhfan.common.utils.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 眭传洪
 * @create 2024-11-14 19:41
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    private final ITokenService tokenService;

    private final StringRedisTemplate stringRedisTemplate;

    private final MultiLevelCache multiLevelCache;

    @Override
    public CaptchaVO generatorCaptcha(Integer width, Integer height) {
        // 生成验证码
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(width, height);
        String code = lineCaptcha.getCode();
        String image = lineCaptcha.getImageBase64();

        // 生成标识并保存到redis
        String uuid = UUID.fastUUID().toString(true);
        String key = RedisConstants.CAPTCHA_UUID_KEY + uuid;
        stringRedisTemplate.opsForValue().set(key, code, RedisConstants.CAPTCHA_TTL, TimeUnit.MINUTES);

        // 返回标识和base64验证码
        return CaptchaVO.builder()
                .codeStr(image)
                .uuid(uuid)
                .build();
    }

    @Override
    public void sendCode(String phoneNumber) {

    }

    @Override
    public UserLoginVO login(LoginFormDTO dto) {
        // 根据不同情况处理
        LoginType loginType = dto.getLoginType();
        User user;
        if (loginType == LoginType.USERNAME_PASSWORD) {
            String userName = dto.getUserName();
            String password = dto.getPassword();
            String captcha = dto.getCaptcha();
            String uuid = dto.getUuid();
            if (StrUtil.hasBlank(userName, password, captcha, uuid)) {
                throw new BizException("用户名，密码, 验证码以及uuid都不能为空");
            }

            // 校验验证码
            String key = RedisConstants.CAPTCHA_UUID_KEY + uuid;
            String cacheCaptcha = stringRedisTemplate.opsForValue().get(key);
            if (StrUtil.isBlank(cacheCaptcha)) {
                throw new BizException("验证码已过期");
            }
            if (!captcha.equals(cacheCaptcha)) {
                // 干掉老验证码
                stringRedisTemplate.delete(key);
                throw new BizException("验证码错误");
            }

            // 获取用户并校验用户
            user = lambdaQuery().eq(User::getUserName, userName).one();
            if (user == null) {
                throw new BizException("没有该用户");
            }
            if (user.getStatus() == UserStatus.STOP_USE) {
                throw new BizException("账号已停用");
            }

            // 校验密码
            String saltPassword = MD5Util.encrypt(password + user.getSalt());
            if (StrUtil.isBlank(saltPassword)) {
                throw new BizException("服务未知错误，请重试");
            }
            if (!saltPassword.equals(user.getPassword())) {
                throw new BizException("密码错误！！！");
            }
        } else {
            String phoneNumber = dto.getPhoneNumber();
            String code = dto.getCode();
            if (StrUtil.hasBlank(phoneNumber, code)) {
                throw new BizException("手机号和验证码都不能为空");
            }

            // 校验验证码是否正确

            // 查询用户信息
            user = lambdaQuery().eq(User::getPhoneNumber, phoneNumber).one();
            if (user == null) {
                // 这里应该是注册用户
                user = new User();
            }
        }

        // 生成token返回
        Map<String, Object> map = new HashMap<>();
        map.put(BizConstants.TOKEN_USER_INFO, user.getId());
        return UserLoginVO.builder()
                .accessToken(tokenService.createAccessToken(map))
                .refreshToken(tokenService.createRefreshToken(map))
                .build();
    }

    @Override
    public UserLoginVO refreshToken(String refreshToken) {
        return tokenService.refreshToken(refreshToken);
    }

    @Override
    public UserInfoVO getUserInfo() {
        // 获取登录用户
        Long userId = UserContext.getUser();

        // 通过多级缓存查询用户信息
        String key = RedisConstants.USER_INFO_CACHE_KEY_PREFIX + userId;
        User user = multiLevelCache.get(key, User.class, () -> getById(userId));
        if (user == null) {
            throw new BizException("用户信息为空");
        }

        return BeanUtil.copyProperties(user, UserInfoVO.class);
    }

    @Override
    public void updateAvatar(Long userId, String avatarUrl) {
        User user = new User();
        user.setId(userId);
        user.setAvatar(avatarUrl);
        updateById(user);
    }

    @Override
    public void updateUserInfo(User user) {
        // 缓存更新策略 先更新数据库 再干掉缓存
        Long userId = UserContext.getUser();
        user.setId(userId);
        updateById(user);

        // 干掉缓存
        String key = RedisConstants.USER_INFO_CACHE_KEY_PREFIX + userId;
        multiLevelCache.deleteCache(key);
    }

    @Override
    public void logout(String refreshToken) {
        // 退出登录 其实把token干掉就可以了
        tokenService.deleteToken(refreshToken);
    }
}
